#!/bin/bash

# Git仓库批量拉取脚本
# 功能：遍历指定目录下的所有子目录，对包含.git目录的仓库执行git pull origin main

# 设置严格模式
# set -euo pipefail

# 显示帮助信息
show_help() {
    cat << EOF
用法: $0 [目录路径] [选项]

功能:
  批量拉取指定目录下的所有git仓库的main分支

参数:
  目录路径    要扫描的目录路径 (必须提供)

选项:
  -h, --help      显示此帮助信息
  -j, --jobs N    并行处理数量 (默认: 1)
  -q, --quiet     静默模式，减少输出
  -v, --verbose   详细输出模式

示例:
  $0 /path/to/projects                  # 拉取指定目录下的所有git仓库
  $0 /path/to/projects -j 4              # 使用4个并行进程
  $0 /path/to/projects --quiet           # 静默模式

注意事项:
  - 需要git命令已安装并可用
  - 只拉取main分支
  - 检查是否有未提交更改
  - 并行处理可提高性能但会增加资源消耗
EOF
}

# 检查目录是否存在
check_directory() {
    if [[ ! -d "$1" ]]; then
        echo "错误: 目录不存在: $1"
        exit 1
    fi
}

# 检查git命令是否可用
check_git_command() {
    if ! command -v git &> /dev/null; then
        echo "错误: git命令未找到，请先安装git" >&2
        exit 1
    fi
}

# 检查工作目录权限和未提交更改
check_working_directory() {
    local repo_dir="$1"
    
    # 检查目录写权限
    if [[ ! -w "$repo_dir" ]]; then
        echo "错误: 目录没有写权限: $repo_dir" >&2
        return 1
    fi
    
    # 检查是否有未提交的更改
    if (cd "$repo_dir" && git status --porcelain | grep -q "^." 2>/dev/null); then
        echo "警告: 发现未提交的更改: $repo_dir" >&2
        echo "请先提交或 stash 这些更改" >&2
        return 1
    fi
    
    return 0
}

# 执行git pull并处理结果
execute_git_pull() {
    local repo_dir="$1"
    local branch="$2"
    local repo_name="$3"
    local quiet="$4"
    
    if [[ "$quiet" != "true" ]]; then
        echo "正在拉取仓库: $repo_name ($branch)"
    fi
    
    local pull_output
    pull_output=$(cd "$repo_dir" && git pull origin "$branch" 2>&1)
    local exit_code=$?
    
    if [[ $exit_code -eq 0 ]]; then
        if [[ "$quiet" != "true" ]]; then
            echo "✓ 仓库 $repo_name 拉取成功"
        fi
        # 如果git pull有输出，显示详细信息
        if [[ -n "$pull_output" && "$quiet" != "true" ]]; then
            echo "$pull_output" | sed 's/^/  /'
        fi
        return 0
    else
        if [[ "$quiet" != "true" ]]; then
            echo "✗ 仓库 $repo_name 拉取失败" >&2
        fi
        # 将git错误输出重定向到stderr
        echo "$pull_output" >&2
        return 1
    fi
}

# 并行处理git仓库
parallel_git_pull() {
    local target_dir="$1"
    local max_jobs="$2"
    local quiet="$3"
    local verbose="$4"
    
    # 创建临时文件来存储结果
    local temp_dir=$(mktemp -d)
    local fifo_file="$temp_dir/fifo"
    mkfifo "$fifo_file"
    
    # 查找所有git仓库
    local repo_dirs=()
    while IFS= read -r -d '' repo_dir; do
        repo_dirs+=("$repo_dir")
    done < <(find "$target_dir" -maxdepth 1 -type d -name "*" -exec test -d {}/.git \; -print0)
    
    # 处理队列
    local pids=()
    local job_count=0
    local repo_index=0
    local total_repos=${#repo_dirs[@]}
    
    while [[ $repo_index -lt $total_repos ]] || [[ ${#pids[@]} -gt 0 ]]; do
        # 清理完成的进程
        local new_pids=()
        for pid in "${pids[@]}"; do
            if ! kill -0 "$pid" 2>/dev/null; then
                wait "$pid"
            else
                new_pids+=("$pid")
            fi
        done
        pids=("${new_pids[@]}")
        
        # 启动新任务
        while [[ ${#pids[@]} -lt $max_jobs && $repo_index -lt $total_repos ]]; do
            local repo_dir="${repo_dirs[$repo_index]}"
            local repo_name=$(basename "$repo_dir")
            
            # 后台执行
            (
                # 检查工作目录
                if ! check_working_directory "$repo_dir"; then
                    echo "跳过仓库: $repo_name (工作目录检查失败)" >&2
                    echo "$repo_name:error:working_directory_check_failed" > "$fifo_file"
                    exit 1
                fi
                
                # 尝试拉取main分支
                if execute_git_pull "$repo_dir" "main" "$repo_name" "$quiet"; then
                    echo "$repo_name:success" > "$fifo_file"
                    exit 0
                else
                    # 尝试其他分支
                    local pull_success=false
                    
                    # 检查默认分支
                    local default_branch=""
                    if (cd "$repo_dir" && git remote show origin 2>/dev/null); then
                        default_branch=$(cd "$repo_dir" && git remote show origin | grep "HEAD branch" | cut -d":" -f2 | tr -d " ")
                    fi
                    
                    # 尝试默认分支
                    if [[ -n "$default_branch" && "$default_branch" != "main" ]]; then
                        if execute_git_pull "$repo_dir" "$default_branch" "$repo_name" "$quiet"; then
                            pull_success=true
                            echo "$repo_name:success" > "$fifo_file"
                            exit 0
                        fi
                    fi
                    
                    # 尝试其他常见分支
                    if [[ "$pull_success" == "false" ]]; then
                        for branch in master develop; do
                            if (cd "$repo_dir" && git show-ref --verify --quiet refs/heads/$branch 2>/dev/null); then
                                if execute_git_pull "$repo_dir" "$branch" "$repo_name" "$quiet"; then
                                    pull_success=true
                                    echo "$repo_name:success" > "$fifo_file"
                                    exit 0
                                fi
                            fi
                        done
                    fi
                    
                    # 如果所有尝试都失败
                    if [[ "$pull_success" == "false" ]]; then
                        echo "✗ 仓库 $repo_name 没有找到可用的分支" >&2
                        echo "$repo_name:error:no_available_branch" > "$fifo_file"
                        exit 1
                    fi
                fi
            ) &
            pids+=("$!")
            ((repo_index++))
            ((job_count++))
            
            # 如果是详细模式，显示进度
            if [[ "$verbose" == "true" && "$quiet" != "true" ]]; then
                echo "已启动任务: $repo_name (任务数: $job_count)"
            fi
        done
        
        # 等待至少一个任务完成
        if [[ ${#pids[@]} -ge $max_jobs ]]; then
            read -r result < "$fifo_file"
        fi
    done
    
    # 等待所有任务完成
    for pid in "${pids[@]}"; do
        wait "$pid"
    done
    
    # 清理临时文件
    rm -rf "$temp_dir"
    
    return 0
}

# 主函数
main() {
    local target_dir="$1"
    local quiet="false"
    local verbose="false"
    local max_jobs=1
    
    echo "开始扫描目录: $target_dir"
    echo "查找所有git仓库并拉取main分支..."
    
    local repo_count=0
    local success_count=0
    local error_count=0
    
    # 使用find命令获取所有git仓库
    while IFS= read -r -d '' repo_dir; do
        ((repo_count++))
        local repo_name=$(basename "$repo_dir")
        
        if [[ "$quiet" != "true" ]]; then
            echo "发现git仓库: $repo_name"
        fi
    done < <(find "$target_dir" -maxdepth 1 -type d -name "*" -exec test -d {}/.git \; -print0)
    
    if [[ $repo_count -eq 0 ]]; then
        echo "没有发现git仓库"
        exit 0
    fi
    
    # 执行并行或串行处理
    if [[ $max_jobs -gt 1 ]]; then
        echo "使用并行处理 (进程数: $max_jobs)"
        parallel_git_pull "$target_dir" "$max_jobs" "$quiet" "$verbose"
    else
        echo "使用串行处理"
        
        # 遍历目标目录的子目录
        for dir in "$target_dir"/*/; do
            if [[ -d "$dir" && -d "$dir/.git" ]]; then
                local dir_name=$(basename "$dir")
                
                # 检查工作目录
                if ! check_working_directory "$dir"; then
                    echo "跳过仓库: $dir_name (工作目录检查失败)" >&2
                    ((error_count++))
                    continue
                fi
                
                # 尝试拉取main分支
                if execute_git_pull "$dir" "main" "$dir_name" "$quiet"; then
                    ((success_count++))
                else
                    # 尝试其他分支
                    local pull_success=false
                    
                    # 检查默认分支
                    local default_branch=""
                    if (cd "$dir" && git remote show origin 2>/dev/null); then
                        default_branch=$(cd "$dir" && git remote show origin | grep "HEAD branch" | cut -d":" -f2 | tr -d " ")
                    fi
                    
                    # 尝试默认分支
                    if [[ -n "$default_branch" && "$default_branch" != "main" ]]; then
                        if execute_git_pull "$dir" "$default_branch" "$dir_name" "$quiet"; then
                            ((success_count++))
                            pull_success=true
                        fi
                    fi
                    
                    # 尝试其他常见分支
                    if [[ "$pull_success" == "false" ]]; then
                        for branch in master develop; do
                            if (cd "$dir" && git show-ref --verify --quiet refs/heads/$branch 2>/dev/null); then
                                if execute_git_pull "$dir" "$branch" "$dir_name" "$quiet"; then
                                    ((success_count++))
                                    pull_success=true
                                    break
                                fi
                            fi
                        done
                    fi
                    
                    # 如果所有尝试都失败
                    if [[ "$pull_success" == "false" ]]; then
                        echo "✗ 仓库 $dir_name 没有找到可用的分支" >&2
                        ((error_count++))
                    fi
                fi
            fi
        done
    fi
    
    # 输出统计信息
    echo ""
    echo "拉取完成！"
    echo "统计信息:"
    echo "  - 发现的git仓库数量: $repo_count"
    echo "  - 成功拉取的仓库数量: $success_count"
    echo "  - 拉取失败的仓库数量: $error_count"
    
    # 返回适当的退出码
    if [[ $error_count -gt 0 ]]; then
        exit 1
    else
        exit 0
    fi
}

# 解析命令行参数
while [[ $# -gt 0 ]]; do
    case "$1" in
        -h|--help)
            show_help
            exit 0
            ;;
        -j|--jobs)
            shift
            if [[ "$1" =~ ^[0-9]+$ ]]; then
                max_jobs="$1"
            else
                echo "错误: -j 选项需要数字参数" >&2
                exit 1
            fi
            ;;
        -q|--quiet)
            quiet="true"
            ;;
        -v|--verbose)
            verbose="true"
            ;;
        *)
            if [[ -z "$TARGET_DIR" ]]; then
                TARGET_DIR="$1"
            else
                echo "错误: 多余的参数: $1" >&2
                exit 1
            fi
            ;;
    esac
    shift
done

# 检查必需参数
if [[ -z "$TARGET_DIR" ]]; then
    echo "错误: 必须提供目录路径参数" >&2
    show_help
    exit 1
fi

# 检查参数
check_directory "$TARGET_DIR"
check_git_command

# 设置默认并行数（如果没有指定）
if [[ -z "$max_jobs" ]]; then
    max_jobs=1
fi

# 限制最大并行数
if [[ $max_jobs -gt 8 ]]; then
    max_jobs=8
    echo "警告: 并行数超过限制，已调整为: $max_jobs" >&2
fi

# 执行主函数
main "$TARGET_DIR"
